<?php
/** @noinspection PhpUnused */

namespace app\admin\controller\shop\order;

use app\admin\controller\shop\Base;
use app\admin\model\shop\order\Order;
use app\admin\model\shop\order\OrderDelivery;
use app\admin\model\shop\order\OrderDeliveryDetail;
use app\admin\model\shop\order\OrderDetail;
use app\shop\enums\delivery\DeliveryMode;
use app\shop\enums\order\OrderDeliveryStatus;
use app\shop\enums\order\OrderPayWay;
use app\shop\enums\order\OrderSource;
use app\shop\enums\order\OrderStatus;
use app\shop\library\ShopHelper;
use app\shop\library\ShopLogHelper;
use app\shop\service\DeliveryService;
use app\shop\service\LogService;
use app\shop\service\OrderService;
use ba\Exception;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use Throwable;

class Delivery extends Base
{
    /**
     * Order模型对象
     * @var object
     * @phpstan-var OrderDelivery
     */
    protected object $model;

    protected array $noNeedPermission = ['company', 'expressTrack'];

    protected string $validateName = \app\admin\validate\shop\order\Delivery::class;

    public function initialize(): void
    {
        parent::initialize();

        $this->model = new OrderDelivery();
    }

    //物流发货
    public function delivery(): void
    {
        $data = $this->request->param();

        $this->validateData($data, 'delivery');

        $orderId = $data['order_id'];

        $orderModel = new Order();
        $order      = $orderModel->where(['id' => $orderId])->with([
            'detail' => function ($query) {
                $query->field(['order_id', 'id', 'num']);
            }
        ])->findOrEmpty();
        if ($order->isEmpty()) {
            $this->error('订单不存在');
        }
        if ($order->status !== OrderStatus::TO_SHIPPED) {
            $this->error('订单状态异常');
        }

        $syncDeliveryInfoResult = true;
        $this->model->startTrans();
        try {
            $this->recordDelivery($data);
            $this->recordOrderDetail($data['delivery_data']);
            $deliveryAll = $this->checkDeliveryAll($data['order_id']);
            $this->recordOrder($data['order_id'], $deliveryAll);
            // 发货模式
            if (count($data['delivery_data']) == count($order->detail)) {
                $order->delivery_mode = DeliveryMode::UNIFIED_DELIVERY;
            } else {
                $order->delivery_mode = DeliveryMode::SPLIT_DELIVERY;
            }
            $order->save();
            // 发货完成
            if ($deliveryAll) {
                (new OrderService())->afterDelivery($data['order_id']);
                // 同步小程序物流信息
                $syncDelivery = ShopHelper::get_setting('sync_delivery');
                if ($syncDelivery && $order->source == OrderSource::MINIAPP && $order->pay_way === OrderPayWay::WXPAY) {
                    $syncDeliveryInfoResult = (new DeliveryService())->syncDeliveryInfoToMiniApp($orderId);
                }
            }

            $this->model->commit();

        } catch (Throwable $e) {
            $this->model->rollback();
            ShopLogHelper::error('发货失败', ['params' => $data], $e);
            $this->error('发货失败', [
                'err' => $e->getMessage()
            ]);
        }

        $message = $syncDeliveryInfoResult ? '发货成功' : '物流信息同步失败,请到小程序发货信息管理模块手动录入发货信息';

        $this->success($message);
    }

    /**
     * 发货物流信息入库
     * @param array $data 物流数据
     * @return void
     * @throws Exception
     */
    private function recordDelivery(array $data): void
    {
        $deliveryData = [
            'order_id'            => $data['order_id'],
            'type'                => $data['type'],
            'delivery_no'         => $data['delivery_no'],
            'delivery_company_id' => $data['delivery_company_id']
        ];

        $deliveryModel    = new OrderDeliveryDetail();
        $saveDeliveryData = [];

        try {
            $delivery = OrderDelivery::create($deliveryData);

            foreach ($data['delivery_data'] as $data) {
                $saveDeliveryData[] = [
                    'delivery_id'     => $delivery['id'],
                    'order_id'        => $delivery['order_id'],
                    'order_detail_id' => $data['order_detail_id'],
                    'goods_id'        => $data['goods_id'],
                    'delivery_num'    => $data['delivery_num'],
                ];
            }

            $deliveryModel->saveAll($saveDeliveryData);
        } catch (\Exception $e) {
            throw new Exception($e->getMessage());
        }
    }

    /**
     * 更新订单详情发货状态
     * @params $data 物流数据
     */
    private function recordOrderDetail(array $deliveryData): void
    {
        $orderDetailModel = new OrderDetail();

        foreach ($deliveryData as $delivery) {
            $orderDetailModel->where(['id' => $delivery['order_detail_id']])->update([
                'delivery_time' => time()
            ]);
            $orderDetailModel->where(['id' => $delivery['order_detail_id']])->inc('delivery_num', $delivery['delivery_num'])->save();
        }
    }

    /**
     * 判断商品是否已经全部发货
     * @param int $order_id
     * @return bool
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    private function checkDeliveryAll(int $order_id): bool
    {
        $orderDetailModel = new OrderDetail();

        $orderDetail = $orderDetailModel->where(['order_id' => $order_id])->select();

        foreach ($orderDetail as $detail) {
            if (is_null($detail->delivery_time)) {
                return false;
            }
            if ($detail->delivery_num < $detail->num) {
                return false;
            }
        }

        return true;
    }

    /**
     * 记录订单状态变更
     * @param int  $orderId
     * @param bool $deliveryAll
     * @return void
     */
    private function recordOrder(int $orderId, bool $deliveryAll): void
    {
        $orderModel = new Order();

        $orderData = [
            'delivery_time' => time()
        ];

        if ($deliveryAll) {
            $orderData['status']          = OrderStatus::TO_RECEIVED;
            $orderData['delivery_status'] = OrderDeliveryStatus::DELIVERED;
            $delivery_msg                 = '订单完成发货';
        } else {
            $orderData['status']          = OrderStatus::TO_SHIPPED;
            $orderData['delivery_status'] = OrderDeliveryStatus::DELIVERY_PART;
            $delivery_msg                 = '订单部分发货';
        }

        $orderModel->where(['id' => $orderId])->update($orderData);

        LogService::orderStatusLog($orderId, $orderData['status'], $delivery_msg, $this->auth->id, true);
    }

    /**
     * 物流轨迹
     * @return void
     * @throws \Exception
     */
    public function expressTrack(): void
    {
        $params = $this->request->param();

        $result = (new DeliveryService())->getDeliveryTracks($params['delivery_no'], $params['delivery_company_id'], $params['phone'], $params['openid']);

        $this->success('', $result);
    }
}